using System;
using System.Collections;
using System.IO;
using Neo.Generator.Core;
using Neo.MetaModel;

namespace Neo.Generator.CodeGen
{
    ///<summary>Code Generator
    ///</summary>
    public class CodeGenerator : VelocityGenerator
    {
        //--------------------------------------------------------------------------------------
        //	fields and constructor
        //--------------------------------------------------------------------------------------

        private string userClassTemplate;
        private string supportFileExtension;
        private string userFileExtension;
        private string supportClassesTemplate;
        private bool generatesUserClasses;
        private bool generatesSupportClasses;
        private bool forcesUserClassGen;
        private string defaultNamespace;

        /// <summary>
        /// Initializes a new instance of the <see cref="CodeGenerator"/> class.
        /// </summary>
        public CodeGenerator()
        {
            userClassTemplate = "NeoClass.vtl";
            userFileExtension = ".cs";
            supportClassesTemplate = "NeoSupport.vtl";
            supportFileExtension = ".cs";
        }


        //--------------------------------------------------------------------------------------
        //	properties
        //--------------------------------------------------------------------------------------

        /// <summary>
        /// Gets or sets the user class template.
        /// </summary>
        /// <value>The user class template.</value>
        public string UserClassTemplate
        {
            set { userClassTemplate = value; }
            get { return userClassTemplate; }
        }

        /// <summary>
        /// Gets or sets the user file extension.
        /// </summary>
        /// <value>The user file extension.</value>
        public string UserFileExtension
        {
            set { userFileExtension = value; }
            get { return userFileExtension; }
        }

        /// <summary>
        /// Gets or sets the support classes template.
        /// </summary>
        /// <value>The support classes template.</value>
        public string SupportClassesTemplate
        {
            set { supportClassesTemplate = value; }
            get { return supportClassesTemplate; }
        }

        /// <summary>
        /// Gets or sets the support file extension.
        /// </summary>
        /// <value>The support file extension.</value>
        public string SupportFileExtension
        {
            set { supportFileExtension = value; }
            get { return supportFileExtension; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [generates user classes].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [generates user classes]; otherwise, <c>false</c>.
        /// </value>
        public bool GeneratesUserClasses
        {
            get { return generatesUserClasses; }
            set { generatesUserClasses = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [generates support classes].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [generates support classes]; otherwise, <c>false</c>.
        /// </value>
        public bool GeneratesSupportClasses
        {
            get { return generatesSupportClasses; }
            set { generatesSupportClasses = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [forces user class gen].
        /// </summary>
        /// <value><c>true</c> if [forces user class gen]; otherwise, <c>false</c>.</value>
        public bool ForcesUserClassGen
        {
            set { forcesUserClassGen = value; }
            get { return forcesUserClassGen; }
        }

        /// <summary>
        /// Gets or sets the default namespace.
        /// </summary>
        /// <value>The default namespace.</value>
        public string DefaultNamespace
        {
            get { return defaultNamespace; }
            set { defaultNamespace = value; }
        }

        //--------------------------------------------------------------------------------------
        //	generate files
        //--------------------------------------------------------------------------------------

        /// <summary>
        /// Generates the specified input file.
        /// </summary>
        /// <param name="inputFile">The input file.</param>
        /// <param name="outputPath">The output path.</param>
        /// <returns></returns>
        public override IList Generate(string inputFile, string outputPath)
        {
            ArrayList files = new ArrayList();
            if (generatesUserClasses)
                files.AddRange(GenerateClassFiles(inputFile, outputPath, true, forcesUserClassGen));
            if (generatesSupportClasses)
                files.AddRange(GenerateClassFiles(inputFile, outputPath, false, true));
            return files;
        }


        /// <summary>
        /// Generates the class files.
        /// </summary>
        /// <param name="inputFile">The input file.</param>
        /// <param name="outputPath">The output path.</param>
        /// <param name="genUser">if set to <c>true</c> [gen user].</param>
        /// <param name="forceWrite">if set to <c>true</c> [force write].</param>
        /// <returns></returns>
        public IList GenerateClassFiles(string inputFile, string outputPath, bool genUser, bool forceWrite)
        {
            IModelReader modelReader;
            ArrayList fileList;
            Entity entity;
            string dir;

            modelReader = (IModelReader) Activator.CreateInstance(readerType);
            fileList = new ArrayList();

            modelReader.ReadConfiguration(inputFile, new ConfigDelegate(setPiAttribute));
            if ((dir = outputPath) == null)
                if ((dir = Path.GetDirectoryName(inputFile)) == null)
                    dir = "";
            modelReader.LoadModel(inputFile);
            if (modelReader.Model.Namespace == null)
                modelReader.Model.Namespace = DefaultNamespace;

            while ((entity = modelReader.GetNextEntity()) != null)
            {
                if (entity.Skip == false)
                {
                    CodeGenerationContext ctx;
                    string subDir, outputFile;

                    ctx = new CodeGenerationContext(entity, this);

                    if (entity.SubPackageName != null)
                        subDir = Path.Combine(dir, entity.SubPackageName);
                    else
                        subDir = dir;
                    outputFile = Path.Combine(subDir, (genUser ? ctx.FileName : ctx.SupportFileName));

                    if ((forceWrite) || (File.Exists(outputFile) == false) || (entity.ForceOverwrite))
                    {
                        Console.WriteLine("Writing {0}", outputFile);
                        using (TextWriter writer = new StreamWriter(outputFile))
                        {
                            Generate(genUser ? userClassTemplate : supportClassesTemplate, ctx, writer);
                        }
                    }
                    fileList.Add(outputFile); 
                }
            }
            return fileList;
        }


        //--------------------------------------------------------------------------------------
        //	generate support classes into a stream
        //--------------------------------------------------------------------------------------

        /// <summary>
        /// Generates the support classes.
        /// </summary>
        /// <param name="inputFile">The input file.</param>
        /// <param name="output">The output.</param>
        public virtual void GenerateSupportClasses(string inputFile, TextWriter output)
        {
            IModelReader modelReader;
            Entity entity;

            modelReader = (IModelReader) Activator.CreateInstance(readerType);
            modelReader.ReadConfiguration(inputFile, new ConfigDelegate(setPiAttribute));
            modelReader.LoadModel(inputFile);
            if (modelReader.Model.Namespace == null)
                modelReader.Model.Namespace = DefaultNamespace;

            while ((entity = modelReader.GetNextEntity()) != null)
            {
                if (entity.Skip == false)
                {
                    CodeGenerationContext ctx = new CodeGenerationContext(entity, this);
                    Generate(supportClassesTemplate, ctx, output);
                }
            }
        }


        //--------------------------------------------------------------------------------------
        //	helper methods
        //--------------------------------------------------------------------------------------

        private void setPiAttribute(string filename, string attr, string val)
        {
            switch (attr)
            {
                case "path":
                    ResourcePath = Path.Combine(Path.GetDirectoryName(filename), val);
                    break;
                case "user":
                    UserClassTemplate = val;
                    break;
                case "userfileextension":
                    UserFileExtension = val;
                    break;
                case "support":
                    SupportClassesTemplate = val;
                    break;
                case "supportfileextension":
                    SupportFileExtension = val;
                    break;
                default:
                    properties[attr] = val;
                    break;
            }
        }
    }
}